﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Runtime.InteropServices;

namespace Parise.Internet.WhoisLite
{
    public class WhoisArinResult
    {
        public string OrgName { get; set; }
        public string OrgID { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string StateProv { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }

        public string NetRange { get; set; }
        public string CIDR { get; set; }
        public string NetName { get; set; }
        public string NetHandle { get; set; }
        public string Parent { get; set; }
        public string NetType { get; set; }
        public string NameServer { get; set; }
        public string Comment { get; set; }
        public string RegDate { get; set; }
        public string Updated { get; set; }

        public string RTechHandle { get; set; }
        public string RTechName { get; set; }
        public string RTechPhone { get; set; }
        public string RTechEmail { get; set; }

        public string OrgTechHandle { get; set; }
        public string OrgTechName { get; set; }
        public string OrgTechPhone { get; set; }
        public string OrgTechEmail { get; set; }

        public override string ToString()
        {
            Type t = typeof(WhoisArinResult);
            return new String(t.GetProperties().SelectMany(p => p.Name + ": " + (string)p.GetValue(this, null) + "\n").ToArray());
        }
    }

    public class Whois
    {
        public const string Arin = "whois.arin.net";

        /// <summary>
        /// Queries a whois server
        /// </summary>
        /// <param name="serverName">Server name (ex: whois.arin.net)</param>
        /// <param name="query">Query to send - IP Address/Domain name</param>
        /// <returns>String result from the server</returns>
        public virtual string Query(string serverName, string query)
        {
            StringBuilder resp = new StringBuilder();
            try
            {
                Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                sock.Connect(new IPEndPoint(Dns.Resolve(serverName).AddressList[0], 43));
                sock.Send(Encoding.ASCII.GetBytes(query + "\r\n"));

                byte[] buff = new byte[1024];
                int recvSize = sock.Receive(buff);
                while (recvSize > 0)
                {
                    resp.Append(Encoding.ASCII.GetString(buff, 0, recvSize));
                    recvSize = sock.Receive(buff);
                }

                if (sock.Connected)
                    sock.Disconnect(false);

                sock.Shutdown(SocketShutdown.Both);
                sock.Close();
            }
            catch
            {
                throw;
            }
            return resp.ToString();
        }

        /// <summary>
        /// Queries a whois server and attempts to parse the values into the given type
        /// </summary>
        /// <typeparam name="T">Type to parse into</typeparam>
        /// <param name="serverName">Server name (ex: whois.arin.net)</param>
        /// <param name="query">Query to send - IP Address/Domain name</param>
        /// <returns>Type T </returns>
        public virtual T QueryAndParse<T>(string serverName, string query) where T : new()
        {

            return ParseResult<T>(Query(serverName, query), ":");
        }

        /// <summary>
        /// Attempts to parse the result value into the given type
        /// </summary>
        /// <typeparam name="T">Type to parse into</typeparam>
        /// <param name="res">Result from a query</param>
        /// <param name="propNameValueDelim">Delimeter between property names and values (ex ":")</param>
        /// <returns>Type T</returns>
        public virtual T ParseResult<T>(string res, string propNameValueDelim) where T : new()
        {
            T b = new T();
            Type t = typeof(T);
            var props = t.GetProperties().Where(p => typeof(string).Equals(p.PropertyType)).ToDictionary(p => p.Name);

            var lines = res.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var line in lines)
            {
                // Split to get name (Should match class)
                var lineInfo = line.Split(new string[] { propNameValueDelim }, StringSplitOptions.RemoveEmptyEntries);

                if (lineInfo.Length > 0 && props.ContainsKey(lineInfo[0]))
                {
                    var cvalue = (string)props[lineInfo[0]].GetValue(b, null);
                    if (!String.IsNullOrEmpty(cvalue))
                        props[lineInfo[0]].SetValue(b, cvalue + "\n" + lineInfo[1].Trim(), null);
                    else
                        props[lineInfo[0]].SetValue(b, lineInfo[1].Trim(), null);
                }
            }

            return b;
        }
    }

}
